import React, {
  useCallback,
  useEffect,
  useLayoutEffect,
  useMemo,
  useRef,
  useState,
} from 'react';
import type {
  MotionEvent,
  MotionEventHandler,
  MotionPrepareEventHandler,
  MotionStatus,
  MotionStep,
  YmCssMotionProps,
} from '../interface';
import {
  STATUS_APPEAR,
  STATUS_ENTER,
  STATUS_LEAVE,
  STATUS_NONE,
  STEP_ACTIVATED,
  STEP_ACTIVE,
  STEP_NONE,
  STEP_PREPARE,
  STEP_START,
} from '../interface';
import useDomMotionEvents from './useDomMotionEvents';

export function isActive(step: MotionStep) {
  return step === STEP_ACTIVE || step === STEP_ACTIVATED;
}

const FULL_STEP_QUEUE: MotionStep[] = [
  STEP_PREPARE,
  STEP_START,
  STEP_ACTIVE,
  STEP_ACTIVATED,
];

export default function useStatus(
  visible: boolean,
  getTargetElement: () => HTMLElement | undefined,
  {
    motionEnter = true,
    motionAppear = true,
    motionLeave = true,
    onAppearPrepare,
    onAppearStart,
    onAppearActive,
    onAppearEnd,
    onEnterPrepare,
    onEnterStart,
    onEnterActive,
    onEnterEnd,
    onLeavePrepare,
    onLeaveStart,
    onLeaveActive,
    onLeaveEnd,
    onVisibleChanged,
  }: Omit<
    YmCssMotionProps,
    | 'visible'
    | 'motionName'
    | 'forceRender'
    | 'removeOnLeave'
    | 'leavedClassName'
    | 'children'
  >,
): [MotionStatus, MotionStep, React.CSSProperties] {
  const [status, setStatus] = useState<MotionStatus>(STATUS_NONE);
  const [step, setStep] = useState<MotionStep>(STEP_NONE);
  const [style, setStyle] = useState<React.CSSProperties>({});
  const mountedRef = useRef(false);
  // const [asyncVisible, setAsyncVisible] = useState<boolean>();
  const animationFrameIdRef = useRef<number>();
  /**
   * Clean up status & style
   */
  function updateMotionEndStatus() {
    setStatus(STATUS_NONE);
    setStep(STEP_NONE);
    setStyle({});
    //动画执行完之后，才能执行
    onVisibleChanged?.(visible);
  }

  // ============================= Step =============================
  const getEventHandlers = (targetStatus: MotionStatus) => {
    switch (targetStatus) {
      case STATUS_APPEAR:
        return {
          [STEP_PREPARE]: onAppearPrepare,
          [STEP_START]: onAppearStart,
          [STEP_ACTIVE]: onAppearActive,
        };

      case STATUS_ENTER:
        return {
          [STEP_PREPARE]: onEnterPrepare,
          [STEP_START]: onEnterStart,
          [STEP_ACTIVE]: onEnterActive,
        };

      case STATUS_LEAVE:
        return {
          [STEP_PREPARE]: onLeavePrepare,
          [STEP_START]: onLeaveStart,
          [STEP_ACTIVE]: onLeaveActive,
        };

      default:
        return {};
    }
  };

  const eventHandlers = useMemo<{
    [STEP_PREPARE]?: MotionPrepareEventHandler;
    [STEP_START]?: MotionEventHandler;
    [STEP_ACTIVE]?: MotionEventHandler;
  }>(() => getEventHandlers(status), [status, step]);

  // ============================ transitionend/animationend监听函数 ============================

  //transitionend/animationend的回调函数
  const onMotionEnd = useCallback(
    (event: MotionEvent) => {
      const element = getTargetElement() as HTMLElement;

      if (event && event.target !== element) return;
      if (status === STATUS_APPEAR) {
        onAppearEnd?.(element, event);
      } else if (status === STATUS_ENTER) {
        onEnterEnd?.(element, event);
      } else if (status === STATUS_LEAVE) {
        onLeaveEnd?.(element, event);
      }
      updateMotionEndStatus();
    },
    [status, getTargetElement],
  );

  const [addMotionEventsListener, removeMotionEventsListener] =
    useDomMotionEvents(onMotionEnd);

  // ============================ Status ============================
  // 根据visible 来设置status
  // 初始化时 visible:true,status:'none' 加载 motionChildren（motionChildren.height = '300px'）,  同时在useEffectLayout中设置 status:'appear' 加载 motionChildren（motionChildren.height = '0px'）而motionChildren 却没有闪一下
  //  通俗来说： useEffect:浏览器的下一帧执行， useLayoutEffect当前帧渲染宏任务执行时，初始化 第二部样式计算(style) 阶段前执行、重绘（repaint）/重排(reflow)前执行。
  useLayoutEffect(() => {
    // console.log('--useLayoutEffect--visible--mountedRef.current--:', visible, mountedRef.current);
    // setAsyncVisible(visible);
    //初始化时, mountedRef.current === false,以后无论visible是true还是false, mountedRef.current每次都是true
    const isMounted = mountedRef.current;
    mountedRef.current = true;

    let nextStatus;

    // Appear
    // 初始化时，visible===true
    if (!isMounted && visible && motionAppear) {
      nextStatus = STATUS_APPEAR;
    }

    // Enter
    if (isMounted && visible && motionEnter) {
      nextStatus = STATUS_ENTER;
    }

    // Leave
    if (isMounted && !visible && motionLeave) {
      nextStatus = STATUS_LEAVE;
    }

    if (nextStatus) {
      setStatus(nextStatus);
      setStep(STEP_PREPARE);
    } else {
      updateMotionEndStatus(); //isMounted === false,visible === false
    }
  }, [visible, motionAppear, motionEnter, motionLeave]);

  // ============================ Step ============================
  // setp 'prepare' -- 'start' -- 'active' --- 'end' 的执行流程，默认是16.7ms递进一次
  //  nextStep: start/active/end
  const doNextStep = useCallback<(nextStep: MotionStep) => void>(
    (nextStep) => {
      function doNext(leftTimes: number) {
        if (leftTimes === 0) {
          window.cancelAnimationFrame(animationFrameIdRef.current!);
          setStep(nextStep);
        } else {
          animationFrameIdRef.current = window.requestAnimationFrame(() => {
            doNext(leftTimes - 1);
          });
        }
      }
      doNext(1);
    },
    [step],
  );

  // useEffect(() => {
  useLayoutEffect(() => {
    if (step !== STEP_NONE && step !== STEP_ACTIVATED) {
      //动画正在进行
      const index = FULL_STEP_QUEUE.indexOf(step);
      const nextStep = FULL_STEP_QUEUE[index + 1];
      if (step === STEP_PREPARE) {
        // 'prepare'
        const onPrepare = eventHandlers[STEP_PREPARE];
        if (typeof onPrepare === 'function') {
          // console.log('--status--step--element--:', status, step, getTargetElement())
          const res = onPrepare(getTargetElement() as HTMLElement);
          if (res instanceof Promise) {
            // step === 'prepare',可以通过Promise--resolve之后再递进
            res.then(() => {
              setStep(nextStep);
            });
          } else {
            doNextStep(nextStep);
          }
        } else {
          doNextStep(nextStep);
        }
      } else {
        // step: start/active阶段是可以给element设置style属性的
        if (step in eventHandlers) {
          setStyle(
            eventHandlers[step]?.(getTargetElement() as HTMLElement) || {},
          );
        }
        if (step === STEP_ACTIVE) {
          //active
          // 开始监听transitionend、animationend事件
          addMotionEventsListener(getTargetElement() as HTMLElement);
        }
        doNextStep(nextStep);
      }
    }
  }, [status, step]);

  // ============================ Effect ============================

  useEffect(() => {
    return () => {
      mountedRef.current = false;
      if (typeof animationFrameIdRef.current === 'number') {
        window.cancelAnimationFrame(animationFrameIdRef.current);
      }
      // removeMotionEventsListener(getTargetElement() as HTMLElement) // 在useDomMotionEvents中执行
    };
  }, []);

  return [status, step, style];
}
